﻿/**
 * Copyright (C) 2013 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NGuice.Inject.Spi;
using System.Reflection;
using AopAlliance.Intercept;
using NGuice.Inject.Internal.Util;
using System.Security;
using System.Collections;
using CGLib;

namespace NGuice.Inject.Internal
{
    /// <summary>
    /// 产生调用类构造器的构造代理的工厂默认实现
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class DefaultConstructionProxyFactory<T> : ConstructionProxyFactory<T>
    {
        private readonly InjectionPoint injectionPoint;

        /// <summary>
        /// </summary>
        /// <param name="injectionPoint">一个成员是T构造器的注入点</param>
        internal DefaultConstructionProxyFactory(InjectionPoint injectionPoint)
        {
            this.injectionPoint = injectionPoint;
        }

        public ConstructionProxy<T> Create()
        {
           // the injection point is for a constructor of T
            ConstructorInfo constructor = (ConstructorInfo)injectionPoint.Member;

            // Use FastConstructor if the constructor is public.
            if (constructor.IsPublic)
            {
                Type classToConstruct = constructor.DeclaringType;
                /*if[AOP]*/
                try
                {
                    //公共构造，使用Fast构造器
                    FastConstructor fastConstructor = BytecodeGen.NewFastClass(classToConstruct).GetConstructor(constructor);
                    return new FastConstructionProxy<T>(this, fastConstructor);
                }
                catch (Exception e)
                {
                    /* fall-through */
                }
                /*end[AOP]*/
            }
            //反射调用，则非公共的构造器也能调用
            return new ReflectConstructionProxy<T>(this);
        }

        private class FastConstructionProxy<T> : ConstructionProxy<T>
        {
            private DefaultConstructionProxyFactory<T> factory;

            private FastConstructor fastConstructor;

            public FastConstructionProxy(DefaultConstructionProxyFactory<T> factory, FastConstructor fastConstructor)
            {
                this.factory = factory;
                this.fastConstructor = fastConstructor;
            }

            public T NewInstance(params object[] arguments)
            {
                return (T)fastConstructor.New(arguments);
            }

            public InjectionPoint InjectionPoint
            {
                get { return factory.injectionPoint; }
            }

            public ConstructorInfo Constructor
            {
                get { return (ConstructorInfo)factory.injectionPoint.Member; }
            }

            public IDictionary<MethodInfo, IList<IMethodInterceptor>> MethodInterceptors
            {
                get { return new Dictionary<MethodInfo, IList<IMethodInterceptor>>(); }
            }
        }

        private class ReflectConstructionProxy<T> : ConstructionProxy<T>
        {
            private DefaultConstructionProxyFactory<T> factory;

            private ConstructorInfo constructor;

            public ReflectConstructionProxy(DefaultConstructionProxyFactory<T> factory)
            {
                this.factory = factory;
                // the injection point is for a constructor of T
                constructor = (ConstructorInfo)factory.injectionPoint.Member;
            }

            public T NewInstance(params object[] arguments)
            {
                try
                {
                    //if (constructor.IsPublic)
                    //{
                        return (T)constructor.Invoke(arguments);
                    //}
                    //else
                    //{
                    //    return (T)constructor.Invoke(BindingFlags.NonPublic, arguments);
                    //}
                }
                catch (MemberAccessException e)
                {
                    throw new AssertionError(e.Message, e); // shouldn't happen, we know this is a concrete type
                }
                catch (SecurityException e)
                {
                    throw new AssertionError(e.Message, e); // a security manager is blocking us, we're hosed
                }
            }

            public InjectionPoint InjectionPoint
            {
                get { return factory.injectionPoint; }
            }

            public ConstructorInfo Constructor
            {
                get { return constructor; }
            }

            public IDictionary<MethodInfo, IList<IMethodInterceptor>> MethodInterceptors
            {
                get { return new Dictionary<MethodInfo, IList<IMethodInterceptor>>(); }
            }
        }
    }
}
